﻿namespace Lilium.CSharp.Expressions

// Translators to F# exprs.
module Translators =

    open System.Linq.Expressions
    
    open Microsoft.FSharp.Quotations
    
    open Lilium.CSharp.Expressions.Patterns

    let private findVariabe scope name = 
        match List.tryPick (Map.tryFind name) scope with
        | Some variable -> variable
        | None -> failwith (sprintf "Variable %s not in scope" name)
        
    let rec private toExprImpl scope expression = 
        match expression with 
        | Call (method', object', arguments) -> Expr.Call (toExprImpl scope object', method', List.map (toExprImpl scope) arguments)
        | Constant (value, type') ->  Expr.Value (value, type')
        | Lambda (body, parameters) -> toLambda scope body parameters
        | Parameter (name, _) -> Expr.Var (findVariabe scope name)
        | _ -> failwith (sprintf "Unable to convert %O to F# expression" expression.NodeType)

    and private toLambda scope body (parameters : ParameterExpression List) =
        match parameters with
        | [] -> Expr.Lambda (Var("unitArg", typeof<unit>), toExprImpl scope body)
        | _  -> 
            let variables = parameters |> List.map (fun p -> (p.Name, Var(p.Name, p.Type)))
            let body = toExprImpl (Map.ofList variables :: scope) body
            List.foldBack (fun (_, var) expr -> Expr.Lambda(var, expr)) variables body

    let toExpr expression = toExprImpl [] expression